package us.versus.them.kitkattoe;

import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.DisplayObject;
import flash.display.Graphics;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.media.Sound;
import flash.text.TextField;
import flash.utils.Timer;

// this bit of bizarreness is to get the Ends enum!
import us.versus.them.kitkattoe.App;

class Game extends Base {
	var app:App;
	var meow:Sound;

	var me:BitmapData;
	var yu:BitmapData;
	var squares:Array< Square >;

	var crs:Bitmap;

	var uTurn:Bool;
	var noMoar:Bool;

	public function new( app, me, yu, meow:Sound, win:Int, lose:Int, tied:Int ) {
		super();
		this.app = app;
		this.me = me;
		this.yu = yu;
		this.meow = meow;

		var catW = me.width;
		var catH = yu.height;

		this.squares = new Array< Square >();
		for ( i in 0... 3 ) {
			for ( j in 0... 3 ) {
				var square = new Square( new BitmapData( catW,catH,false,0xFFFF00 ) );
				square.x = catW / 5 + j * ( catW + catW / 3 );
				square.y = catH / 3 + i * ( catH + catH / 3 );
				this.squares.push( square );
				this.addChild( square );
			}
		}

		var i = 0;
		for ( square in this.squares ) {
			this.addClick( square, i++ );
		}

		if ( Math.random() < 0.5 ) { 
			this.catMove();
		}
		
		this.urTurn( true );
		this.noMoar = false;
		
		var wins = Base.text( 'h3', '', 'wins:' + win,   10, -4, Base.DEFAULT_STYLESHEET );
		var tide = Base.text( 'h3', '', 'tied:' + tied, 150, -4, Base.DEFAULT_STYLESHEET );
		var loss = Base.text( 'h3', '', 'loss:' + lose, 300, -4, Base.DEFAULT_STYLESHEET );

		addChild( wins );
		addChild( tide );
		addChild( loss );

		this.crs = new Bitmap( yu );
		this.crs.x = this.crs.y = -1000;
		addChild( crs );
	}

	public function addClick( square:Square, idx:Int ) {
		var thiz = this;
		square.addEventListener( 
			MouseEvent.CLICK
			, callback( function( mouseEvent:MouseEvent ) {
				thiz.click( idx );
			} )
			, false, 0, false
		);
	}

	public function move( player:BitmapData, idx:Int ) {
		this.squares[ idx ].take( player );

		var winner = this.winner( this.squares, true );
		if ( winner == this.me ) {
			this.gameOver( Ends.me );
			this.noMoar = true;
		} 
		if ( winner == this.yu ) {
			this.gameOver( Ends.yu );
			this.noMoar = true;
		} 

		var all = true;
		for ( square in this.squares ) {
			if ( ! ( square.taken() ) ) {
				all = false;
				break;
			}
		}
		if ( all ) {
			// cat got the game, yo...
			this.gameOver( Ends.ti );
			this.noMoar = true;
		}
	}

	public function gameOver( end ) {
		this.urTurn( false );
		var thiz = this;

		var timer = new Timer( 2000, 1 );

		var cb = function(e:Dynamic) { 
			thiz.app.gameOver( end ); 
			timer.stop();
		};
		this.removeEventListeners();	
		timer.addEventListener( TimerEvent.TIMER , callback( cb ) );
		addEventListener( MouseEvent.MOUSE_DOWN, callback( cb ) );
		timer.start();
	}

	public function click( idx:Int ) {
		if ( this.uTurn ) { 
			if ( !this.squares[ idx ].taken() ) {
				this.urTurn( false );
				this.move( this.yu, idx );
				this.catMove();
			}
		}
	}

	public override function draw( g:Graphics ) {
        this.removeEventListener( Event.ENTER_FRAME, enter_frame );
		g.clear();
	}

	public function winner( squares:Array< Square >, ?hi:Bool ) { 
		var winner;
		// across
		for ( i in 0...3 ) {
			winner = check( squares, i * 3 + 0 , i * 3 + 1 , i * 3 + 2, hi );
			if ( null != winner ) return winner;
		}
		// down
		for ( i in 0...3 ) {
			winner = check( squares, i + 0 , i + 3 , i + 6, hi );
			if ( null != winner ) return winner;
		}

		// diag \
		winner = check( squares, 0, 4, 8, hi );
		if ( null != winner ) return winner;

		// diag /	
		winner = check( squares, 2, 4, 6, hi );
		if ( null != winner ) return winner;

		return null;
	}

	public function check( squares:Array< Square >, a:Int,b:Int,c:Int, ?hi:Bool ) {
		var winner = null;
		if (
			null != squares[ a ].holds()
			&& squares[ a ].holds() == squares[ b ].holds() 
			&& squares[ c ].holds() == squares[ b ].holds()
		) { 
			winner = squares[ a ].holds();
			if ( hi ) {
				this.hi( squares[ a ] );
				this.hi( squares[ b ] );
				this.hi( squares[ c ] );
			}
		}
		return winner;
	}

	public function hi( square:Square ) {
		var n = 8;
		this.graphics.lineStyle( n * 2, 0xFF0000 );
		this.graphics.drawRoundRectComplex(
			  square.x
			, square.y
			, square.width
			, square.height
			, n
			, n
			, n
			, n
		);
	}

	public function copy( squares:Array< Square > ) {
		var nu_squares = new Array< Square >();
		for ( square in squares ) {
			nu_squares.push( square.copy() );
		}
		return nu_squares;
	}

	//////////////////////////
	// behold the awesome brain of my virtual cat!!!

	public function catMove() {
		if ( this.noMoar ) return; // don't take last move, cheater!

		var possible_moves = [];
		var i = 0;
		for ( square in this.squares ) {
			if ( !square.taken() ) {
				possible_moves.push( i );
			}
			i++;
		}

		if ( 0 == possible_moves.length ) {
			this.gameOver( Ends.ti );
		} else {
			var choice : Int = -1;
			for ( idx in possible_moves ) {
				// try to win:
				if ( this.wins( idx, this.squares ) ) {
					choice = idx;
					break;
				}
			}
			if ( -1 == choice ) {
				for ( idx in possible_moves ) {
					// try to block:
					if ( this.blocks( idx, this.squares ) ) {
						choice = idx;
						break;
					}
				}
				if ( -1 == choice ) {
					choice = possible_moves[ Math.floor( Math.random() * possible_moves.length ) ];
				}
			}

			this.move( this.me, choice );
			this.urTurn( true );
		}
	}

	// only looks ahead 1 move...
	public function wins( idx:Int, squares:Array< Square > ) {
		var tmp = this.copy( squares );
		tmp[ idx ].take( this.me );
		return this.me == this.winner( tmp );
	}

	public function blocks( idx:Int, squares:Array< Square > ) {
		var tmp = this.copy( squares );
		tmp[ idx ].take( this.yu );
		return this.yu == this.winner( tmp );
	}

	////

	public function mouse_move( mouseEvent:MouseEvent ) {
		if ( this.uTurn ) {
			this.crs.x = mouseEvent.localX + mouseEvent.target.x -  this.crs.width / 2;
			this.crs.y = mouseEvent.localY + mouseEvent.target.y - this.crs.height / 2;
		} else {
			this.crs.x = this.crs.y = -1000;
		}
	}

	public function urTurn( uTurn:Bool ) {
		this.uTurn = uTurn;
		if ( this.uTurn ) {
			addEventListener( MouseEvent.MOUSE_MOVE, mouse_move );
		} else {
			this.removeEventListener( MouseEvent.MOUSE_MOVE, mouse_move );
			this.crs.x = this.crs.y = -1000;
		}
	}

}
